/**
 *  Copyright (C) 2013  Piotr Szczepa�ski
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.pw.elka.gtsna.test_performance;

import java.util.concurrent.TimeUnit;

import edu.pw.elka.gtsna.centrality.betweenness.ProbabilisticSVNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.SVNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.SuccessiveGroupNodeBetweenness;
import edu.pw.elka.gtsna.graph.Graph;
import edu.pw.elka.gtsna.graph.UndirectedGraph;
import edu.pw.elka.gtsna.graph_creator.ScaleFreeGraphGenerator;
import edu.pw.elka.gtsna.montecarlo.MonteCarloShapleyValue;
import edu.pw.elka.gtsna.ranking.Ranking;
import edu.pw.elka.gtsna.utilities.Agregator;
import edu.pw.elka.gtsna.utilities.MathPowers;
import edu.pw.elka.gtsna.utilities.Timer;

/**
 * @author Piotr Lech Szczepa�ski
 * @author P.Szczepanski@ii.pw.edu.pl 
 *
 */
public class SVBCPerformer  extends Performer  {

	// exact time
	Agregator[] MCTimes = new Agregator[2001];
	Agregator exactTime = new Agregator();

	Agregator exactTime2 = new Agregator();
	
	
	int numberOfGraphs = 1;
	int numberOfMC = 1;
	
	int graphSize = 400;
	int maxIterations = 10000;
	int MCstep=10;
	
	SVBCPerformer(){
	}
	
	/**
	 * @param threads
	 */
	SVBCPerformer(int threads) {
		super(threads);
	}

	static int index(double error){
		return (int) Math.min(Math.round(error*100),2000);
	}
	
	// GRAPH_SIZE, NUMBER_OF_GRAPHS, MC_STEP, MC_ITERATIONS, NUMBER_OF_THREADS
	public static void main(String[] args) {
		if (args.length < 5) {
			System.out.println("Missing arguments: "+"GRAPH_SIZE,  NUMBER_OF_GRAPHS, MC_STEP, MC_ITERATIONS, NUMBER_OF_THREADS");
			return;
		}

		SVBCPerformer performer = new SVBCPerformer(Integer.parseInt(args[4]));
		performer.setGraphSize(Integer.parseInt(args[0]));
		performer.setNumberOfGraphs(Integer.parseInt(args[1]));
		performer.setMCstep(Integer.parseInt(args[2]));
		performer.setMaxIterations(Integer.parseInt(args[3]));
		performer.runTest();
	}

	
	public void runTest(){
		System.out.println("running");
		for (int i=0; i<2001; i++){
			MCTimes[i]= new Agregator();
		}
		
		
		final Graph[] gr = new Graph[numberOfGraphs];
		
		final Timer exactT = new Timer();	
		final Timer[] mcT = new Timer[numberOfGraphs];
		
		for (int i= 0; i<numberOfGraphs; i++){
			gr[i] = new UndirectedGraph(new ScaleFreeGraphGenerator(graphSize,(int)Math.sqrt(graphSize)));
			mcT[i] = new Timer();
		}
		
		MathPowers.precompute(graphSize);
		
		for (int i= 0; i<numberOfGraphs; i++){
			final int j = i;
			executorService.execute(new Runnable() {
			    public void run() {
			    	
			    	//	Graph gr = new UndirectedGraph(new GMLReader("power.gml"));
		    		SuccessiveGroupNodeBetweenness gS = new SuccessiveGroupNodeBetweenness(gr[j]);
		    		gS.preProcess();			
		    		
		    		SVNodeBetweenness SV = new SVNodeBetweenness(gr[j]);
		    		exactT.start();
		    		SV.computeCentrality();
		    		exactT.stop();
		    		System.out.println("PS Exact: "+sixDForm.format(exactT.getTime()));
		    		exactTime.observe(exactT.getMilliTime());
		    		exactT.reset();
		    		
		    		ProbabilisticSVNodeBetweenness PSV = new ProbabilisticSVNodeBetweenness(gr[j]);
		    		exactT.start();
		    		PSV.computeCentrality();
		    		exactT.stop();
		    		System.out.println("PSV Exact: "+sixDForm.format(exactT.getTime()));
		    		exactTime2.observe(exactT.getMilliTime());
		    		exactT.reset();

		    		
		    		
		    		//System.out.println("Exact: "+exactTime.getMean());
		    		for (int mcI = 0 ; mcI < numberOfMC ; mcI++){
		    			//System.out.println("GRAPH: "+j+"\\"+numberOfGraphs+"   MC ITER: "+mcI+"\\"+numberOfMC);
		    			MonteCarloShapleyValue MC = new MonteCarloShapleyValue(gS, maxIterations);
		    			for (int iter=0; iter<maxIterations; iter+=MCstep){
		    				System.out.println("GRAPH: "+j+"\\"+numberOfGraphs+"      INNER ITER: "+iter+"\\"+maxIterations);
		    				mcT[j].start();
		    				MC.computeSolution(MCstep);
		    				mcT[j].stop();
		    				MCTimes[index(Ranking.relativeError(MC.partialSolution(), SV.getCentralities()))].observe(mcT[j].getTime());
		    				//System.out.println(index(Ranking.relativeError(MC.partialSolution(), SV.getCentralities()))+" "+sixDForm.format(mcT.getTime()));
		    			}
		    			mcT[j].reset();
		    		}   
			    }
			});
		}
		
		executorService.shutdown();

		
		try {
				executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
			} catch (InterruptedException e) {
				System.out.println("ERROR: PROCESS INTERUPTED");
			}
		
		
		System.out.println("EXACT: "+sixDForm.format(exactTime.getMean())+" "+sixDForm.format(exactTime.getLowerBound(75))+" "+sixDForm.format(exactTime.getUpperBound(75)));
		System.out.println("EXACT2: "+sixDForm.format(exactTime2.getMean())+" "+sixDForm.format(exactTime2.getLowerBound(75))+" "+sixDForm.format(exactTime2.getUpperBound(75)));
		
//		for (int i=0; i<2001; i++){
//			double j = i;
//			if (MCTimes[i].getN()!=0){
//				System.out.println(j+" "+sixDForm.format(MCTimes[i].getMean())+" "+sixDForm.format(MCTimes[i].getLowerBound(75))+" "+sixDForm.format(MCTimes[i].getUpperBound(75)));
//			}
//		}
	}

	public int getNumberOfGraphs() {
		return numberOfGraphs;
	}

	public void setNumberOfGraphs(int numberOfGraphs) {
		this.numberOfGraphs = numberOfGraphs;
	}

	public int getGraphSize() {
		return graphSize;
	}

	public void setGraphSize(int graphSize) {
		this.graphSize = graphSize;
	}

	public int getMaxIterations() {
		return maxIterations;
	}

	public void setMaxIterations(int maxIterations) {
		this.maxIterations = maxIterations;
	}

	public int getMCstep() {
		return MCstep;
	}

	public void setMCstep(int mCstep) {
		MCstep = mCstep;
	}
	


	
}
